1. 回调函数

       面试题:回调函数有什么缺点?如何解决回调地狱问题?

       回调容易写出回调地狱,根本问题是嵌套函数存在耦合性,一旦有所改动就会牵一发而动全身,并且嵌套函数多了会很难处理错误。回调函数不能使用 try catch 捕获错误,不能直接 return。

       如何解决回调地狱的问题?:用 Generator/Promise/async&&await 来改善。

2. Generator

       面试题:你理解的 Generator 是什么??

       Generator 是生成器,一种返回迭代器(是一种特殊对象,具有一些专门为迭代过程设计的专有接口,所有的迭代器对象都有 next()方法,每次调用都会返回结果对象{value:’xxx’,done:true/false})的函数,用处是可以控制函数的执行,通过 function 关键字后的*来表示,函数中会使用 yield 关键字。

1
2
3
4
5
6
7
8
9
10
function *foo(x) {
let y = 2 * (yield (x + 1));
let z = yield (y / 3);
return (x + y + z);
}

let it = foo(5);
console.log(it.next()); // 执行yield(x+1) => 输出{value:6, done:false}
console.log(it.next(12)); // 执行yield(y/3),传入的12是上一个yield的返回值(如果不传参数,上一个yield是undefined),即 yield(x+1) = 12 => y = 2*12 = 24; => 输出yield(y/3) = {value:8, done:false}
console.log(it.next(13)); // 执行x+y+z,输入的13是yield(y/3) => z = 13 => 输出x+y+z = 5+24+13 = 42

       使用 Generator 来解决回调地狱的问题:

1
2
3
4
5
6
7
8
9
function *fetch() {
yield ajax(url1 ,() => {})
yield ajax(url2 ,() => {})
yield ajax(url3 ,() => {})
}
let it = fetch();
let result1 = it.next();
let result2 = it.next();
let result3 = it.next();

3.Promise

       面试题:Promise 的特点是什么,分别有什么优缺点?什么是 Promise 链?Promise 构造函数执行和 then 函数执行有什么区别?

  1. 特点和优缺点:
  • Promise 有三种状态:等待中(pending)、完成了(resolved)、拒绝了(rejected),一旦从等待状态变成了其他状态就不能更改状态了
1
2
3
4
new Promise((resolve, reject) => {
resolve('success')
reject('reject') // 无效
})
  • 在构造 Promise 的时候,构造函数内部的代码是立即执行的
1
2
3
4
5
6
new Promise((resolve, reject) => {
console.log('new Promise');
resolve('success');
})
console.log('finish');
// new Promise -> finish

       缺点:无法取消 Promise,错误需要通过回调函数捕获。

  1. Promise 实现了链式调用,每次调用 then 之后返回一个全新的Promise(因为状态不可变),如果在 then 中使用了 return,那么 return 的值会被 Promise.resolve()包装。

  2. 使用 Promise 来解决回调地狱的问题:

1
2
3
4
5
6
7
8
9
10
ajax(url)
.then(res => {
console.log(res)
return ajax(url1)
}).then(res => {
console.log(res)
return ajax(url2)
}).then(res => {
return console.log(res)
})

4.async 及 await

       面试题:async 及 await 的特点,它们的优点和缺点分别是什么?await 原理是什么?

  • await 就是 generator 加上 Promise 的语法糖,且内部实现了自动执行 generator。一个函数如果加上 async,那么函数就会返回一个 Promise,async 就是将函数返回值用 RESOLVE 包裹了,和 then 中处理返回值一样,并且 await 只能配套 async 使用
  • 相比于直接使用 Promise,处理掉了 then 的调用链,优雅的解决了回调地狱的问题
1
2
3
4
5
6
7
async function test() {
// 以下代码没有依赖性的话,完全可以使用 Promise.all 的方式
// 如果有依赖性的话,其实就是解决回调地狱的例子了
await fetch(url)
await fetch(url1)
await fetch(url2)
}
  • 缺点:await 将异步代码改造成了同步代码,如果多个异步没有依赖性,使用了 await 会导致性能上的降低。(一个执行完成才会执行下一个)
    await例子

           await 创建 promise,所以 await 之后的代码才是异步的。